// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

#ifndef FLASH_H_
#define FLASH_H_

#include <stddef.h>
#include <stdint.h>
#include "status/rot_status.h"


#define	FLASH_REGION_MASK(x)			(~((x) - 1))
#define	FLASH_REGION_BASE(x, size)		((x) & FLASH_REGION_MASK (size))
#define	FLASH_REGION_OFFSET(x, size)	((x) & ((size) - 1))


/**
 * API for interfacing with a flash device.
 */
struct flash {
	/**
	 * Get the size of the flash device.
	 *
	 * @param flash The flash to query.
	 * @param bytes Output for the number of bytes in the device.
	 *
	 * @return 0 if the device size was successfully read or an error code.
	 */
	int (*get_device_size) (struct flash *flash, uint32_t *bytes);

	/**
	 * Read data from flash.
	 *
	 * @param flash The flash to read from.
	 * @param address The address to start reading from.
	 * @param data The buffer to hold the data that has been read.
	 * @param length The number of bytes to read.
	 *
	 * @return 0 if the bytes were read from flash or an error code.
	 */
	int (*read) (struct flash *flash, uint32_t address, uint8_t *data, size_t length);

	/**
	 * Get the size of a flash page for write operations.
	 *
	 * @param flash The flash to query.
	 * @param bytes Output for the number of bytes in a flash page.
	 *
	 * @return 0 if the page size was successfully read or an error code.
	 */
	int (*get_page_size) (struct flash *flash, uint32_t *bytes);

	/**
	 * Get the minimum number of bytes that must be written to a single flash page.  Writing fewer
	 * bytes than the minimum to any page will still result in a minimum sized write to flash.  The
	 * extra bytes that were written must be erased before they can be written again.
	 *
	 * @param flash The flash to query.
	 * @param bytes Output for the minimum number of bytes for a page write.
	 *
	 * @return 0 if the minimum write size was successfully read or an error code.
	 */
	int (*minimum_write_per_page) (struct flash *flash, uint32_t *bytes);

	/**
	 * Write data to flash.  The flash region being written to needs to be erased prior to writing.
	 *
	 * Writes are achieved most efficiently if they align to page boundaries.
	 *
	 * @param flash The flash to write to.
	 * @param address The address to start writing to.
	 * @param data The data to write.
	 * @param length The number of bytes to write.
	 *
	 * @return The number of bytes written to the flash or an error code.  Use ROT_IS_ERROR to check
	 * the return value.
	 */
	int (*write) (struct flash *flash, uint32_t address, const uint8_t *data, size_t length);

	/**
	 * Get the size of a flash sector for erase operations.
	 *
	 * @param flash The flash to query.
	 * @param bytes Output for the number of bytes in a flash sector.
	 *
	 * @return 0 if the sector size was successfully read or an error code.
	 */
	int (*get_sector_size) (struct flash *flash, uint32_t *bytes);

	/**
	 * Erase a single sector of flash.
	 *
	 * @param flash The flash to erase.
	 * @param sector_addr An address within the sector to erase.  The erase operation will erase the
	 * entire sector that contains the specified address.
	 *
	 * @return 0 if the sector was erased or an error code.
	 */
	int (*sector_erase) (struct flash *flash, uint32_t sector_addr);

	/**
	 * Get the size of a flash block for erase operations.
	 *
	 * @param flash The flash to query.
	 * @param bytes Output for the number of bytes in a flash block.
	 *
	 * @return 0 if the block size was successfully read or an error code.
	 */
	int (*get_block_size) (struct flash *flash, uint32_t *bytes);

	/**
	 * Erase a block of flash.
	 *
	 * @param flash The flash to erase.
	 * @param block_addr An address within the block to erase.  The erase operation will erase the
	 * entire block that contains the specified address.
	 *
	 * @return 0 if the block was erased or an error code.
	 */
	int (*block_erase) (struct flash *flash, uint32_t block_addr);

	/**
	 * Erase the entire flash device.
	 *
	 * @param flash The flash to erase.
	 *
	 * @return 0 if the all flash memory was erased or an error code.
	 */
	int (*chip_erase) (struct flash *flash);
};


#define	FLASH_ERROR(code)		ROT_ERROR (ROT_MODULE_FLASH, code)

/**
 * Error codes that can be generated by a flash device.
 */
enum {
	FLASH_INVALID_ARGUMENT = FLASH_ERROR (0x00),		/**< Input parameter is null or not valid. */
	FLASH_NO_MEMORY = FLASH_ERROR (0x01),				/**< Memory allocation failed. */
	FLASH_DEVICE_SIZE_FAILED = FLASH_ERROR (0x02),		/**< Failed to determine flash device size. */
	FLASH_READ_FAILED = FLASH_ERROR (0x03),				/**< Failed to read data from flash. */
	FLASH_PAGE_SIZE_FAILED = FLASH_ERROR (0x04),		/**< Failed to determine page write size. */
	FLASH_WRITE_FAILED = FLASH_ERROR (0x05),			/**< Failed to write data to flash. */
	FLASH_SECTOR_SIZE_FAILED = FLASH_ERROR (0x06),		/**< Failed to determine sector erase size. */
	FLASH_SECTOR_ERASE_FAILED = FLASH_ERROR (0x07),		/**< Failed to erase a flash sector. */
	FLASH_BLOCK_SIZE_FAILED = FLASH_ERROR (0x08),		/**< Failed to determine block erase size. */
	FLASH_BLOCK_ERASE_FAILED = FLASH_ERROR (0x09),		/**< Failed to erase a flash block. */
	FLASH_CHIP_ERASE_FAILED = FLASH_ERROR (0x0a),		/**< Failed to erase the entire flash device. */
	FLASH_ADDRESS_OUT_OF_RANGE = FLASH_ERROR (0x0b),	/**< Invalid address provided for the request. */
	FLASH_NOT_BLANK = FLASH_ERROR (0x0c),				/**< The flash is expected to be blank but is not. */
	FLASH_HW_NOT_INIT = FLASH_ERROR (0x0d),				/**< The flash hardware interface was not initialized. */
	FLASH_MINIMUM_WRITE_FAILED = FLASH_ERROR (0x0e),	/**< Failed to determine the minimum write size. */
};


#endif /* FLASH_H_ */
